Chapter 9

Making Your Program Flow


CONTENTS


To achieve more than the simplest "Hello World" task in any programming language, you need to get into some real programming. Just as trying to hold a conversation in French knowing only Bonjour can be somewhat limiting, programming in VBScript can be difficult knowing only Alert "Hello World".

To write scripts or programs that achieve a goal or provide a solution, you first need to clearly define the task at hand. Because VBScript is a logical and easy-to-use language, you will soon see that the definition you have come up with for your problem can be translated directly into a program.

But writing modern event-driven programs with VBScript is not a straightforward, start-to-finish affair. Many times, you need the program to make decisions and, based on that decision, maybe jump to another part of the program. Or, perhaps, a calculation is required to produce a value you use regularly in the program, so your program jumps off in another direction to get that value and then comes back to continue. This is where managing program flow becomes vital.

In this chapter, you'll see the main constituents of program flow. Program flow is dictated by calls to functions and procedures, loops, and decisions-parts of the program that force execution to be either diverted somewhere else in the program or held in a certain part for a given period. Before this gets too cryptic, let's move on and see this in action.

Subroutines

Subroutines are small, almost self-contained programs that are called by another part of the program as they are required. The term subroutine can be thought of as a general term for the individually defined sections of the overall script.

Subroutines in VBScript include the following:

Declaring Custom Functions

Functions are separate subroutines that return a value-simple as that. Declaring a function is very straightforward:

Function myFunctionName()
.....
End Function

You use the keyword Function, followed by the name you've given to the function. The parentheses are used to hold the variable names for any values being passed into the function from outside, which is covered later in this section. To complete the function definition, use the words End Function. The following is a quick example, with the result shown in Figure 9.1.

Figure 9.1 : The custom function (myfunction.htm) in the browser.

<HTML>
<HEAD>
<TITLE>My Function</TITLE>
<SCRIPT LANGUAGE="vbscript">
 Sub myButton_OnClick
  myVar = Document.Form1.Input.Value
  Document.Form1.Output.Value = myFunction(myVar)
 End Sub

 Function myFunction(ByVal AnyValue)
   myFunction = AnyValue & " added by my function"
 End Function

</SCRIPT>
</HEAD>
<BODY BGCOLOR="white">
<CENTER>
<FORM NAME="Form1">
Input  &nbsp;<INPUT TYPE="text" NAME="Input"><P>
<INPUT TYPE="button" NAME="myButton" VALUE="Click Me"><P>
Output &nbsp;<INPUT TYPE="text" NAME="Output" SIZE=40></FORM
</CENTER>
</BODY>
</HTML>

In this example, execution of the script commences when you click the button. This fires an OnClick event, which is handled by the myButton_OnClick event handler, shown here:

Sub myButton_OnClick
 myVar = Document.Form1.Input.Value
 Document.Form1.Output.Value = myFunction(myVar)
End Sub

The value in the Input text box is copied into the myVar variable. The next line basically says to the program, "Go and do myFunction, take myVar with you and bring back the result; then show that result in the Output text box." So, off it goes to myFunction, taking myVar with it. Here's the myFunction function:

Function myFunction(ByVal AnyValue)
  myFunction = AnyValue & " added by my function"
End Function

This function has been defined to receive one variable, which can be any variable. The ByVal keyword tells the scripting engine that it wants to receive just the value of the passed variable. If you try to call this function without any variables (or arguments), like

x = myFunction()

or if you call it with more than one argument, like

x = myFunction(y, a, I, strMyString)

you generate a runtime error. The number of arguments (or variables) must be the same. The names you use for the argument are irrelevant, but the order in which they appear is the same. Here is an example:

a = 10
b = 20
c = 30
d = 40
x = anyFunction(a,b,c,d)

Function anyFunction(e,f,g,h)

Within the anyFunction function, the values are e = 10, f = 20, g = 30, and h = 40.

To return the result of the function to the code that called it, you use the function name as the left side of an assignment, like this:

myFunction = AnyValue & " added by my function"

When the execution reaches the End Function, the value that has been assigned to the function is passed back to the calling script as the result.

But why not just put the code in line with the rest of the program? Well, it really depends on what you are trying to achieve. Let's say you use a particular calculation in many different parts of the program. Each time you use the calculation, you have to enter the same lines of code. It would be much easier to enter the code once in a function, and then call the function each time you need to use the calculation. Another benefit of a function is that it makes the code easier to read. Separating the code that performs a calculation makes for a tidier, less cluttered appearance. Look at the following "before and after" example:

<SCRIPT LANGUAGE="vbscript">
Sub cmdButton1_OnClick
 x = CDbl(Document.Form1.Text1.Value)
 y = CDbl(Document.Form1.Text2.Value)
 z = x * 34 + y
 b = z / (35 * 9) + 1
 i = b ^ 789 - 93
 Document.Form1.Text3.Value = CStr(i)
End Sub

Sub cmdButton2_OnClick
 x = CDbl(Document.Form1.Text4.Value)
 y = CDbl(Document.Form1.Text5.Value)
 z = x * 80 + y
 b = z / (35 * 9) + 1
 i = b ^ 789 - 93
 Document.Form1.Text6.Value = CStr(i)
End Sub

Sub cmdButton3_OnClick
 x = CDbl(Document.Form1.Text7.Value)
 y = 989
 z = x * 20 + y
 b = z / (35 * 9) + 1
 i = b ^ 789 - 93
 Document.Form1.Text9.Value = CStr(i)
End Sub

</SCRIPT>

In this totally imaginary script, each of three buttons has a similar event handler. But notice the subtle differences in each one. The following is how it could be rescripted using a single function:

<SCRIPT LANGUAGE="vbscript">

Function myFunction(ByVal x, ByVal y, ByVal v)
 x = CDbl(x)
 y = CDbl(y)
 z = x * v + y
 b = z / (35 * 9) + 1
 i = b ^ 789 - 93
 myFunction = CStr(i)
End Function

Sub cmdButton1_OnClick
 Document.Form1.Text3.Value = myFunction(Document.Form1.Text1.Value, 
åDocument.Form1.Text2.Value, 34)
End Sub

Sub cmdButton2_OnClick
 Document.Form1.Text6.Value = myFunction(Document.Form1.Text4.Value, 
åDocument.Form1.Text5.Value, 80)
End Sub

Sub cmdButton3_OnClick
 Document.Form1.Text9.Value = myFunction(Document.Form1.Text7.Value, 989, 20)
End Sub

</SCRIPT>

Not only does it look tidier, but it is also easier to maintain. Any modification to the formula needs to be made in only one place rather than three, which reduces the chance of error.

Declaring Custom Procedures

A custom procedure is a subroutine that doesn't return a value. Like functions, procedures can accept variables from other parts of the program. To declare a custom procedure, you use the Sub keyword, followed by the name of the procedure.

Sub myProcedure()
....
End Sub

You can use two types of syntax to call a custom procedure. The first method is to use the keyword Call, like this:

Call myProcedure()

If you use the keyword Call, you must use parentheses around the argument list or use empty parentheses. The other method is to simply use the procedure name without the word Call, like this:

myProcedure

If you omit the keyword Call, you cannot use parentheses around your argument list.

The following example uses both methods. The result is shown in Figure 9.2.

Figure 9.2 : The custom procedure, proc.htm, in the browser.

<HTML>
<HEAD>
<TITLE>My Custom Procedure</TITLE>
<SCRIPT LANGUAGE="vbscript">
Sub Button1_OnClick
 Call DoComplexMessage("First Message")
End Sub

Sub Button2_OnClick
 DoComplexMessage "Second Message"
End Sub

Sub DoComplexMessage(ByVal Message)
 Dim CRLF
 Dim strTitle
 Dim strMainMessage
 CRLF = Chr(10) & Chr(13)
 strTitle = "My Custom Procedure"

 strMainMessage = "Hello this is the " & Message & CRLF
 strMainMessage = strMainMessage & "This is my procedure to show " & CRLF
 strMainMessage = strMainMessage & "a message box which is similar " & CRLF
 strMainMessage = strMainMessage & "from both buttons"

 x = MsgBox(strMainMessage,0,strTitle)

End Sub

</SCRIPT>
</HEAD>
<BODY BGCOLOR="white">
<CENTER>
<INPUT TYPE="button" NAME="Button1" VALUE="Button 1">
<INPUT TYPE="button" NAME="Button2" VALUE="Button 2">
</CENTER>
</BODY>
</HTML>

Both event handlers for the buttons call the DoComplexMessage procedure. One uses Call and parentheses, and the other simply uses the procedure name. As you can clearly see, the use of a custom procedure in this example removes the need for you to write the same code more than once.

Using the ActiveX Control Pad to Create Subroutines

The ActiveX Control Pad enables you to quickly create custom procedures and functions, and it also makes it easy to add the calls to your custom procedures and functions.

To see how this works, let's re-create the example used in the last section using the ActiveX Control Pad.

  1. Open the ActiveX Control Pad and, using the HTML template provided, create the HTML part of the page, which should now look like what you see in Figure 9.3.
    Figure 9.3 : The custom procedure example's HTML.
  2. Now open the Script Wizard by clicking the script button on the toolbar or selecting Script Wizard from the Tools menu.
  3. To create a custom procedure, right-click anywhere in the right actions pane, and select New Procedure from the pop-up menu. The Script Wizard then opens a new custom procedure in the script pane and gives it a default name of Procedure1(). (See Figure 9.4.)
    Figure9.4: The new custom procedure.
  4. Rename the procedure by changing Procedure1 to DoComplexMessage, and add ByVal Message between the parentheses.
  5. Enter the following code into the script pane:Dim CRLF
 Dim strTitle
 Dim strMainMessage
 CRLF = Chr(10) & Chr(13)
 strTitle = "My Custom Procedure"

 strMainMessage = "Hello this is the " & Message & CRLF
 strMainMessage = strMainMessage & "This is my procedure to show " & CRLF
 strMainMessage = strMainMessage & "a message box which is similar " & 			åCRLF
 strMainMessage = strMainMessage & "from both buttons"

 x = MsgBox(strMainMessage,0,strTitle)
  1. Click the plus sign that appears next to the word Procedures in the right actions pane, and you can see that your new custom procedure is available to be used by any of the events. Your Script Wizard should now resemble the one in Figure 9.5.
    Figure 9.5 : The completed custom procedure.
  2. To call the custom procedure from the buttons events, click the plus sign to the left of Button1 in the left events frame, and select the OnClick event.
  3. Double-click the DoComplexMessage procedure in the right actions pane. Automatically, the Script Wizard adds the words Call DoComplexMessage() to the script pane.
  4. Edit the statement by adding "First Message" between the parentheses. Your Script Wizard now looks like the one in Figure 9.6.
    Figure 9.6 : Calling the procedure from the OnClick event.
  5. Repeat this for Button2, using "Second Message" between the parentheses.
  6. To finish, click OK and the Script Wizard automatically generates the code, which looks like this:
<HTML>
<HEAD>
    <SCRIPT LANGUAGE="VBScript">
<!--
Sub DoComplexMessage(ByVal Message)
 Dim CRLF
 Dim strTitle
 Dim strMainMessage
 CRLF = Chr(10) & Chr(13)
 strTitle = "My Custom Procedure"

 strMainMessage = "Hello this is the " & Message & CRLF
 strMainMessage = strMainMessage & "This is my procedure to show "
å & CRLF
 strMainMessage = strMainMessage & "a message box which is similar "
å & CRLF
 strMainMessage = strMainMessage & "from both buttons"

 x = MsgBox(strMainMessage,0,strTitle)
end sub
-->
    </SCRIPT>
<TITLE>My Custom Procedure</TITLE>
</HEAD>
<BODY BGCOLOR="white">
<CENTER>
    <INPUT LANGUAGE="VBScript" TYPE=button VALUE="Button 1" 
åOncLICK="call DoComplexMessage(&quot;First Message&quot;)"
     NAME="Button1">
    <INPUT LANGUAGE="VBScript" TYPE=button VALUE="Button 2"
åOncLICK="call DoComplexMessage(&quot;Second Message&quot;)"
     NAME="Button2">
</CENTER>
</BODY>
</HTML>

Note that the custom procedure code is identical to the one you created manually earlier. However, the Script Wizard places event handlers for HTML Intrinsic controls within the HTML definition of the control. Save the file and test it in the browser.

NOTE
You can also create custom functions using the ActiveX Script Wizard. Follow the previous instructions, and simply replace the word Sub with Function. When the Script Wizard generates the code, it will automatically place End Function rather than End Sub at the end of the script.

Making Decisions with If and Select

Adding some pseudo-intelligence to your script is possible only if you can enable your script to make decisions. You can add decision-making capabilities in two ways.

If...Then Else and ElseIf

The If...Then conditional statement is the most widely used construct in any programming language. It is easy and quick to use and enables you to branch the program based on the result of the condition, like this:

If myVariable = yourVariable Then
     Alert "got a match"
End

If...Then statements can also be nested, which means you can have a condition that is tested only if the condition of the first statement is true. Here is an example:

If myVariable = yourVariable Then
    If aVariable = bVariable Then
        Alert "Both sets match"
    End If
End If

For the code within the conditional statement to execute, the condition must evaluate True.

if x = 10 then
    Alert "yes, x does equal 10"
end if

But what happens if it evaluates to False? Well, normally execution continues with the code directly after the End If statement. However, you can add an Else statement within the condition, which makes the code execute only if the condition is False. Here is an example:

If x = 10 then
    Alert "Yes, x does equal 10"
Else
    Alert "No, x does not equal 10"
End If

You can even add a further condition within the Else section. Just use the ElseIf keyword, like this:

If x = 10 then
    Alert "Yes, x does equal 10"
ElseIf x = 20 Then
    Alert "x does not equal 10 but it equals 20"
Else
    Alert "x does not equal 10 or 20"
End If

You can turn the condition on its head and have the code execute only if the condition evaluates to False. This is done by including the negation operator Not.

If Not x = 10 Then
 Alert "Sorry but x does not equal 10"
End If

Note that, in truth, the overall statement still evaluates to True. Essentially, the statement says, "Does x not equal 10? Yes, it doesn't."

Select Case

The other type of conditional statement you have at your disposal is the Select Case block. With Select Case, you start with a known value and then use the Case statement to compare whether the case variable matches the selection variable. If the comparison evaluates to true, the line following Case is executed. After execution, the program continues with the line following End Select.

Select Case x
Case 5
    Alert "x equals 5"
Case 10
    Alert "x equals 10"
Case 15
    Alert "x equals 15"
End Select

Again, let's look at how you can have at least one line of the block execute no matter what the result. You use Case Else, like this:

Select Case x
Case 5
    Alert "x equals 5"
Case 10
    Alert "x equals 10"
Case 15
    Alert "x equals 15"
Case Else
    Alert "I dont care what value x is but its not 5, 10 or 15"
End Select

NOTE
Unlike Select Case in Visual Basic, the VBScript version does not allow statements like Case Is < 8 or Case Is 10 To 100. All values for the Case statements must be explicit, which restricts its usage drastically.

Looping with For and Do

What do you do if you need to execute the same section of code many times over, possibly with a slight change to the code? You could write the code over and over again, like this:

x = 0
Document.Write x
Document.Write x + 1
Document.Write x + 2
Document.Write x + 3
Document.Write x + 4
Document.Write x + 5

This could continue on, and copying and pasting it wouldn't take too long to reach 100! But there's a much easier method to achieve the same result. It's known as a loop.

VBScript gives you two ways in which you can repeat the same code as many times as you want:

For...Next Loops

A For...Next loop enables you to repeat execution of the code held within the loop a given number of times. You specify the upper and lower parameters of the loop counter, and the scripting engine increments the loop counter automatically as the loop is executed.

For x = 1 to 100
  do some code 100 times
Next

NOTE
VB programmers should keep in mind that in VBScript, you can use only Next. You do not have the option to use the old Next x as you do in VB4.

By default, the loop counter is incremented by one every time the program reaches the Next statement, until the upper limit is reached, at which point the program continues execution with the line following Next.

Let's have a look at a few variations on the For...Next theme. First, what if you want the counter to increment in reverse, or maybe increment by more than one each time? There's a special keyword called Step. Step is used to change the behavior of the increment. If Step is negative, the loop goes backward, like so:

For x = 100 to 0 Step -1
    Do something 100 times
Next

In this example, x starts at 100 and is reduced by 1 with every loop until it reaches 0, at which point the loop is terminated. Step can be any number, and that number then becomes the increment.

For x = 0 to 100 Step 2
    Do something 50 times
Next

Second, what if you want to initially loop a certain number of times, but jump out of the loop if a particular condition is met? For this, you use Exit For. Exit For takes the execution out of the loop and resumes the program at the line immediately following the Next statement.

For x = 0 to 50
 y = y + 1
  If y = z then
   Exit For
  End If
Next

Do...Loop

Another way you can repeat the same code over and over is to use a Do...Loop statement, like this:

Do
code to execute for ever
Loop

But, actually, it's not like this at all. If you wrote that into your script, your script would never finish. You need to give the loop a condition so that it knows when to stop repeating. The two conditions are While and Until.

A Do While loop repeats while a condition is True, as in the following example:

x = 2
Do While x < 10
 x = x + 1
Loop

A Do Until loop repeats until a condition is True, as in the following example:

Do Until x = 10
 x = x + 1
Loop

As with the For...Next loop, you have several options that you can use with the Do...Loop statement. The first is Exit Do. Again, this is to allow an exit route for your program in case the need arises.

Do While x < 100
 x = x + 1
 z = z - 1
  If z = 50
   Exit Do
  End if
Loop

The next option is to place the While or Until on the same line as Loop, rather than on the same line as Do, like this:

Do
 x = x + 1
Loop Until x = 100

The difference is that placing While or Until next to Loop forces the statement block to be executed at least once. If the condition is placed next to Do, and the condition is True as the program arrives at the statement block, then the loop is not executed. Here is an example:

x = 200
Do While x < 100
 This code never executes
Loop

However, if the code was rewritten like the following, the code executes once:

x = 200
Do
 This Code executes only once
Loop While x < 100

Workshop Wrap-Up

In this chapter, you've been through the main language components that enable you to control the flow within your script. From defining subroutines and functions, to decision-making statements, and finally how to execute code over and over. Here's a brief summary:

Next Steps

Now you know how to create scripts that can make decisions, branch to other parts of the script, repeat sections of code, and generally perform like they know what they're doing. You can add even more functionality to your scripts by looking at the following chapters:

Q&A

Q:
How do you decide when to subdivide a program into smaller sections?
A:
Many of the reasons for creating separate subroutines and functions will give you an idea about when it is right to create a separate subroutine. Subroutines save coding. If you find yourself coding the same lines over and over, you probably need a subroutine. If you have a routine that returns a value, and that value might (now or in future) be needed by several parts of the program, or if the routine to produce the value is more than a few lines of code, I would suggest that it be placed in its own function.